package com.hys.app.service.erp.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hys.app.converter.erp.OrderReturnConverter;
import com.hys.app.converter.erp.OrderReturnItemConverter;
import com.hys.app.framework.database.WebPage;
import com.hys.app.framework.database.mybatisplus.base.BaseServiceImpl;
import com.hys.app.framework.exception.ServiceException;
import com.hys.app.framework.rabbitmq.MessageSender;
import com.hys.app.framework.rabbitmq.MqMessage;
import com.hys.app.framework.util.CurrencyUtil;
import com.hys.app.framework.util.DateUtil;
import com.hys.app.framework.util.PageConvert;
import com.hys.app.mapper.erp.OrderReturnMapper;
import com.hys.app.model.base.rabbitmq.AmqpExchange;
import com.hys.app.model.erp.dos.*;
import com.hys.app.model.erp.dto.OrderReturnDTO;
import com.hys.app.model.erp.dto.OrderReturnItemDTO;
import com.hys.app.model.erp.dto.OrderReturnQueryParams;
import com.hys.app.model.erp.dto.OrderReturnStatisticsQueryParam;
import com.hys.app.model.erp.dto.message.OrderReturnAuditPassMessage;
import com.hys.app.model.erp.enums.*;
import com.hys.app.model.erp.vo.OrderReturnAllowable;
import com.hys.app.model.erp.vo.OrderReturnStatistics;
import com.hys.app.model.erp.vo.OrderReturnVO;
import com.hys.app.model.system.dos.AdminUser;
import com.hys.app.model.system.dos.DeptDO;
import com.hys.app.service.erp.*;
import com.hys.app.service.system.AdminUserManager;
import com.hys.app.service.system.DeptManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.function.Function;

import static com.hys.app.framework.util.CollectionUtils.convertList;
import static com.hys.app.framework.util.CollectionUtils.convertMap;

/**
 * 订单退货业务层实现
 *
 * @author 张崧
 * @since 2023-12-14 15:42:07
 */
@Service
public class OrderReturnManagerImpl extends BaseServiceImpl<OrderReturnMapper, OrderReturnDO> implements OrderReturnManager {

    @Autowired
    private OrderReturnConverter converter;

    @Autowired
    private OrderReturnItemConverter itemConverter;

    @Autowired
    private OrderReturnItemManager orderReturnItemManager;

    @Autowired
    private DeptManager deptManager;

    @Autowired
    private WarehouseManager warehouseManager;

    @Autowired
    private OrderManager orderManager;

    @Autowired
    private WarehouseOutItemManager warehouseOutItemManager;

    @Autowired
    private NoGenerateManager noGenerateManager;

    @Autowired
    private OrderReturnMapper orderReturnMapper;

    @Autowired
    private WarehouseEntryBatchManager batchManager;

    @Autowired
    private MessageSender messageSender;

    @Autowired
    private AdminUserManager adminUserManager;

    @Autowired
    private FinanceItemManager financeItemManager;

    @Autowired
    private OrderItemManager orderItemManager;

    @Override
    public WebPage<OrderReturnVO> list(OrderReturnQueryParams queryParams) {
        WebPage<OrderReturnDO> webPage = baseMapper.selectPage(queryParams);

//        List<Long> deptIds = convertList(webPage.getData(), OrderReturnDO::getDeptId);
//        Map<Long, String> deptNameMap = deptService.listAndConvertMap(deptIds, DeptDO::getId, DeptDO::getName);
//
//        List<Long> warehouseIds = convertList(webPage.getData(), OrderReturnDO::getWarehouseId);
//        Map<Long, String> warehouseNameMap = warehouseManager.listAndConvertMap(warehouseIds, WarehouseDO::getId, WarehouseDO::getName);

        return converter.convert(webPage);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(OrderReturnDTO orderReturnDTO) {
        check(orderReturnDTO);

        // 保存退货单
        OrderReturnDO orderReturnDO = converter.combination(orderReturnDTO);
        orderReturnDO.setSn(noGenerateManager.generate(NoBusinessTypeEnum.OrderReturn, orderReturnDTO.getDeptId()));
        orderReturnDO.setStatus(OrderReturnStatusEnum.NotSubmit);
        save(orderReturnDO);

        // 保存退货单明细
        List<OrderReturnItemDO> itemList = itemConverter.combination(orderReturnDO.getId(), orderReturnDTO);
        orderReturnItemManager.saveBatch(itemList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(OrderReturnDTO orderReturnDTO) {
        OrderReturnDO oldDO = getById(orderReturnDTO.getId());
        if (!new OrderReturnAllowable(oldDO).getEdit()) {
            throw new ServiceException("不能进行编辑操作");
        }

        check(orderReturnDTO);

        // 保存退货单
        OrderReturnDO orderReturnDO = converter.combination(orderReturnDTO);
        updateById(orderReturnDO);

        // 保存退货单明细
        List<OrderReturnItemDO> itemList = itemConverter.combination(orderReturnDO.getId(), orderReturnDTO);
        orderReturnItemManager.deleteByOrderReturnId(Collections.singletonList(orderReturnDTO.getId()));
        orderReturnItemManager.saveBatch(itemList);
    }

    @Override
    public OrderReturnVO getDetail(Long id) {
        OrderReturnVO orderReturnVO = converter.convert(getById(id));
        List<OrderReturnItemDO> itemList = orderReturnItemManager.listByOrderReturnId(id);
        orderReturnVO.setItemList(itemConverter.convert(itemList));
        return orderReturnVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(List<Long> ids) {
        List<OrderReturnDO> orderReturnList = listByIds(ids);
        for (OrderReturnDO orderReturnDO : orderReturnList) {
            if (!new OrderReturnAllowable(orderReturnDO).getDelete()) {
                throw new ServiceException(orderReturnDO.getSn() + "不允许进行删除操作");
            }
        }

        removeBatchByIds(ids);
        orderReturnItemManager.deleteByOrderReturnId(ids);
    }

    @Override
    public void submit(Long id) {
        // 校验
        OrderReturnDO oldDO = getById(id);
        if (!new OrderReturnAllowable(oldDO).getSubmit()) {
            throw new ServiceException("不允许进行提交操作");
        }

        lambdaUpdate()
                .set(OrderReturnDO::getStatus, OrderReturnStatusEnum.Submit)
                .eq(OrderReturnDO::getId, id)
                .update();
    }

    @Override
    public void withdraw(Long id) {
        // 校验
        OrderReturnDO oldDO = getById(id);
        if (!new OrderReturnAllowable(oldDO).getWithdraw()) {
            throw new ServiceException("不允许进行提交操作");
        }

        lambdaUpdate()
                .set(OrderReturnDO::getStatus, OrderReturnStatusEnum.NotSubmit)
                .eq(OrderReturnDO::getId, id)
                .update();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void audit(List<Long> ids, OrderReturnStatusEnum status, String remark) {
        if (status != OrderReturnStatusEnum.AuditPass && status != OrderReturnStatusEnum.AuditReject) {
            throw new ServiceException("审核参数错误");
        }

        List<OrderReturnDO> orderReturnList = listByIds(ids);
        for (OrderReturnDO orderReturnDO : orderReturnList) {
            // 校验
            if (!new OrderReturnAllowable(orderReturnDO).getAudit()) {
                throw new ServiceException(orderReturnDO.getSn() + "不能进行审核操作");
            }
        }

        AdminUser currUser = adminUserManager.getCurrUser();
        lambdaUpdate()
                // 如果是驳回,状态改为未提交状态
                .set(OrderReturnDO::getStatus, status == OrderReturnStatusEnum.AuditReject ? OrderReturnStatusEnum.NotSubmit : status)
                .set(OrderReturnDO::getAuditRemark, remark)
                .set(OrderReturnDO::getAuditById, currUser.getId())
                .set(OrderReturnDO::getAuditBy, currUser.getRealName())
                .in(OrderReturnDO::getId, ids)
                .update();

        // 如果审核通过
        if (status == OrderReturnStatusEnum.AuditPass) {
            for (OrderReturnDO orderReturnDO : orderReturnList) {
                List<OrderReturnItemDO> returnItemList = orderReturnItemManager.listByOrderReturnId(orderReturnDO.getId());

                // 因为不是退回原来的仓库，所以需要新创建批次
                List<WarehouseEntryBatchDO> batchList = itemConverter.combination(returnItemList, orderReturnDO, noGenerateManager);
                batchManager.create(StockChangeSourceEnum.ORDER_RETURN, orderReturnDO.getSn(), batchList);

                // 更新已退货数量
                for (OrderReturnItemDO orderReturnItemDO : returnItemList) {
                    if (orderReturnDO.getOrderType() == OrderTypeEnum.TO_B) {
                        warehouseOutItemManager.increaseReturnNum(orderReturnItemDO.getWarehouseOutItemId(), orderReturnItemDO.getReturnNum());
                    } else {
                        orderItemManager.increaseReturnNum(orderReturnItemDO.getOrderItemId(), orderReturnItemDO.getReturnNum());
                    }
                }

                // 生成财务明细
                Double totalPrice = CurrencyUtil.sum(returnItemList, itemDO -> CurrencyUtil.add(itemDO.getProductPrice(), itemDO.getReturnNum()));
                financeItemManager.addExpand(FinanceExpandTypeEnum.OrderReturn, orderReturnDO.getSn(), totalPrice);
            }

            // 发送审核通过消息
            OrderReturnAuditPassMessage message = new OrderReturnAuditPassMessage();
            message.setList(orderReturnList);
            this.messageSender.send(new MqMessage(AmqpExchange.ORDER_RETURN_AUDIT_PASS, AmqpExchange.ORDER_RETURN_AUDIT_PASS + "_ROUTING", message));
        }
    }

    /**
     * 查询订单退货统计分页列表数据
     *
     * @param params 查询参数
     * @return
     */
    @Override
    public WebPage statistics(OrderReturnStatisticsQueryParam params) {
        IPage<OrderReturnStatistics> iPage = this.orderReturnMapper.selectOrderReturnPage(new Page(params.getPageNo(), params.getPageSize()), params);
        return PageConvert.convert(iPage);
    }

    /**
     * 导出订单退货统计列表数据
     *
     * @param response
     * @param params   查询参数
     */
    @Override
    public void export(HttpServletResponse response, OrderReturnStatisticsQueryParam params) {
        //查询库存报损单商品列表
        List<OrderReturnStatistics> list = this.orderReturnMapper.selectOrderReturnList(params);

        ArrayList<Map<String, Object>> rows = CollUtil.newArrayList();
        ExcelWriter writer = ExcelUtil.getWriter(true);
        for (OrderReturnStatistics statistics : list) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("所属分部", statistics.getDeptName());
            map.put("仓库名称", statistics.getWarehouseName());
            map.put("销售经理", statistics.getDistributionName());
            map.put("退货单编号", statistics.getSn());
            map.put("订单编号", statistics.getOrderSn());
            map.put("退货时间", DateUtil.toString(statistics.getReturnTime(), "yyyy-MM-dd HH:mm:ss"));
            map.put("入库单号", statistics.getWarehouseEntrySn());
            map.put("商品编号", statistics.getProductSn());
            map.put("商品名称", statistics.getProductName());
            map.put("商品类别", statistics.getCategoryName());
            map.put("规格型号", statistics.getProductSpecification());
            map.put("单位", statistics.getProductUnit());
            map.put("退货数量", statistics.getReturnNum());
            rows.add(map);
        }

        writer.write(rows, true);

        ServletOutputStream out = null;
        try {
            out = response.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=order_return.xlsx");
        writer.flush(out, true);
        writer.close();
        IoUtil.close(out);
    }

    private void check(OrderReturnDTO orderReturnDTO) {
        OrderDO orderDO = orderManager.getById(orderReturnDTO.getOrderId());
        if (orderDO == null) {
            throw new ServiceException("订单不存在");
        }
        boolean isToC = orderDO.getType() == OrderTypeEnum.TO_C;
        if (!isToC && !orderDO.getShipFlag()) {
            throw new ServiceException("只能选择已发货的订单");
        }
        if (isToC && orderDO.getStatus() != OrderStatusEnum.COMPLETE) {
            throw new ServiceException("只能选择已完成的订单");
        }
        if (isToC) {
            // 零售订单校验订单项
            Map<Long, OrderItemDO> orderItemMap = checkOrderItem(orderReturnDTO);
            orderReturnDTO.setOrderItemMap(orderItemMap);
            // 查询订单项的批次信息
            List<Long> batchIds = convertList(orderItemMap.values(), OrderItemDO::getBatchId);
            List<WarehouseEntryBatchDO> batchList = batchManager.listByIds(batchIds);
            Map<Long, WarehouseEntryBatchDO> batchMap = convertMap(batchList, WarehouseEntryBatchDO::getId, Function.identity());
            orderReturnDTO.setBatchMap(batchMap);
        } else {
            // 销售订单校验出库单项
            Map<Long, WarehouseOutItemDO> warehouseOutItemMap = checkWarehouseOutItem(orderReturnDTO);
            orderReturnDTO.setWarehouseOutItemMap(warehouseOutItemMap);
        }

        DeptDO deptDO = deptManager.getDept(orderReturnDTO.getDeptId());
        if (deptDO == null) {
            throw new ServiceException("部门不存在");
        }

        WarehouseDO warehouseDO = warehouseManager.getById(orderReturnDTO.getWarehouseId());
        if (warehouseDO == null) {
            throw new ServiceException("仓库不存在");
        }

        AdminUser handleBy = adminUserManager.getModel(orderReturnDTO.getHandleById());
        if (handleBy == null) {
            throw new ServiceException("经手人不存在");
        }

        orderReturnDTO.setOrderDO(orderDO);
        orderReturnDTO.setDeptDO(deptDO);
        orderReturnDTO.setWarehouseDO(warehouseDO);
        orderReturnDTO.setHandleBy(handleBy);
    }

    private Map<Long, WarehouseOutItemDO> checkWarehouseOutItem(OrderReturnDTO orderReturnDTO) {
        List<Long> warehouseOutItemIdIds = convertList(orderReturnDTO.getItemList(), OrderReturnItemDTO::getWarehouseOutItemId);
        Map<Long, WarehouseOutItemDO> warehouseOutItemMap = warehouseOutItemManager.listAndConvertMap(warehouseOutItemIdIds, WarehouseOutItemDO::getId);

        for (OrderReturnItemDTO returnItemDTO : orderReturnDTO.getItemList()) {
            WarehouseOutItemDO warehouseOutItemDO = warehouseOutItemMap.get(returnItemDTO.getWarehouseOutItemId());
            if (warehouseOutItemDO == null) {
                throw new ServiceException("出库单项：" + returnItemDTO.getWarehouseOutItemId() + "不存在");
            }
            if ((warehouseOutItemDO.getReturnNum() + returnItemDTO.getReturnNum()) > warehouseOutItemDO.getOutNum()) {
                throw new ServiceException(warehouseOutItemDO.getProductName() + "可退货数量不足");
            }
        }

        return warehouseOutItemMap;
    }

    private Map<Long, OrderItemDO> checkOrderItem(OrderReturnDTO orderReturnDTO) {
        if (orderReturnDTO.getItemList().stream().anyMatch(orderReturnItemDTO -> orderReturnItemDTO.getOrderItemId() == null)) {
            throw new ServiceException("订单项id不能为空");
        }

        List<Long> orderItemIds = convertList(orderReturnDTO.getItemList(), OrderReturnItemDTO::getOrderItemId);
        List<OrderItemDO> orderItemList = orderItemManager.listByIds(orderItemIds);
        Map<Long, OrderItemDO> orderItemMap = convertMap(orderItemList, OrderItemDO::getId, Function.identity());
        for (OrderReturnItemDTO returnItemDTO : orderReturnDTO.getItemList()) {
            Long orderItemId = returnItemDTO.getOrderItemId();
            OrderItemDO orderItemDO = orderItemMap.get(orderItemId);
            if (orderItemDO == null) {
                throw ServiceException.format("订单项{}不存在", orderItemId);
            }
            if (!orderItemDO.getOrderId().equals(orderReturnDTO.getOrderId())) {
                throw ServiceException.format("订单项{}不属于当前订单", orderItemId);
            }
            if ((orderItemDO.getReturnNum() + returnItemDTO.getReturnNum()) > orderItemDO.getNum()) {
                throw ServiceException.format("商品【{}】可退货数量不足", orderItemDO.getProductName());
            }
        }

        return orderItemMap;
    }
}

